//==============================================================================================================
//	|		   >>>>>>>>>>>>>>>>>> LIBRARIES <<<<<<<<<<<<<<<<<<			|
//==============================================================================================================
#include "config.h"
//------------------------------------------------------------------------------------------------------------
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
//------------------------------------------------------------------------------------------------------------
#include "define_constructions.h"
#include "ex_mem_t.h"
//==============================================================================================================
//==============================================================================================================


//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
//||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
//	|		  	 >>>>>>>>>>>>>>>>>> CODE <<<<<<<<<<<<<<<<<<			|
//||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
//;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;


//==============================================================================================================
struct ex_mem_t *ex_mem_constructor (const int target_ex_mem_size)
{
  INCR_INDENT;
  INDENT (OUT ("# called ex_mem_constructor\n"));

  assert (target_ex_mem_size > 0); // shortest memcall program need 7 bytes

  ex_mem_t *ret_mem = (ex_mem_t *)calloc(1, sizeof(struct ex_mem_t));
  assert (ret_mem);

  char temp_name[] = "mem_page_XXXXXXXX";
  int fd = mkstemp (temp_name);
  assert (fd != -1);

  lseek (fd, target_ex_mem_size+1, SEEK_SET);
  write (fd, "", 1);
  lseek (fd, 0, target_ex_mem_size);

  ret_mem -> data = mmap (0, target_ex_mem_size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, fd, 0);
  memset (ret_mem -> data, 0xc3, target_ex_mem_size);

  assert (ret_mem -> data);

  close (fd);
  unlink (temp_name);

  ret_mem -> data_size = target_ex_mem_size;

  DECR_INDENT;
  return ret_mem;
}

//==============================================================================================================
void ex_mem_destructor (struct ex_mem_t *target_mem)
{
  INCR_INDENT;
  INDENT (OUT ("# called ex_mem_destructor [ %p ]\n", target_mem));

  assert (target_mem);
  assert (target_mem->data);

  munmap (target_mem->data, target_mem->data_size);
  target_mem -> data = NULL;

  const short int POISON = 666;
  target_mem -> data_size = POISON;

  free (target_mem);
  DECR_INDENT;
}

//==============================================================================================================
short int ex_mem_execute (const struct ex_mem_t *target_mem)
{
  INCR_INDENT;
  INDENT (OUT ("# called ex_mem_execute [ %p ]->[ %p ]\n", target_mem, target_mem -> data));

  assert (target_mem);
  assert (target_mem -> data);

  asm volatile  (
        "call *%%ESI\n"
        :
        : "S"(target_mem->data)
        : "eax", "ebx", "ecx", "edx", "edi"
        );

  DECR_INDENT;
  return 0;
}

//==============================================================================================================
short int ex_mem_dump (const struct ex_mem_t *target_mem, FILE *target_dev)
{
  INCR_INDENT;
  INDENT (OUT ("# called ex_mem_dump\n"));

  assert (target_mem);
  assert (target_dev);

  char *cure_mem_pos = (char *)target_mem -> data;
  char *mem_end = cure_mem_pos + target_mem->data_size;

  fprintf (target_dev,
           "\n"
           "###############################################################################\n"
           "            >>>>> MEMORY [%p ... %p] CONTENT: <<<<<\n"
           "-------------------------------------------------------------------------------\n",
           cure_mem_pos, mem_end);


  addr_u i386_cmd;

  while (cure_mem_pos < mem_end)
    {
      i386_cmd.addr = (void *)(*cure_mem_pos++);
      fprintf (target_dev,"%02x ", i386_cmd.addr_byte[0]);
    }

  fprintf (target_dev,
           "\n"
           "###############################################################################\n\n");

  DECR_INDENT;
  return 0;
}

